<?php
class jsHelper {
    protected $_controller,
              $_action,
              $_View;
    public $selection;
    public $jQueryObject = '$';

    protected $_minimizedAttributeFormat = '%s="%s"';

    protected $_minimizedAttributes = array(
        'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected',
        'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize',
        'autoplay', 'controls', 'loop', 'muted', 'required', 'novalidate', 'formnovalidate'
    );
    protected $_attributeFormat = '%s="%s"';
    protected $_callbackArguments = array(
        'slider' => array(
            'start' => 'event, ui',
            'slide' => 'event, ui',
            'change' => 'event, ui',
            'stop' => 'event, ui'
        ),
        'sortable' => array(
            'start' => 'event, ui',
            'sort' => 'event, ui',
            'change' => 'event, ui',
            'beforeStop' => 'event, ui',
            'stop' => 'event, ui',
            'update' => 'event, ui',
            'receive' => 'event, ui',
            'remove' => 'event, ui',
            'over' => 'event, ui',
            'out' => 'event, ui',
            'activate' => 'event, ui',
            'deactivate' => 'event, ui'
        ),
        'drag' => array(
            'start' => 'event, ui',
            'drag' => 'event, ui',
            'stop' => 'event, ui',
        ),
        'drop' => array(
            'activate' => 'event, ui',
            'deactivate' => 'event, ui',
            'over' => 'event, ui',
            'out' => 'event, ui',
            'drop' => 'event, ui'
        ),
        'request' => array(
            'beforeSend' => 'XMLHttpRequest',
            'error' => 'XMLHttpRequest, textStatus, errorThrown',
            'success' => 'data, textStatus',
            'complete' => 'XMLHttpRequest, textStatus',
            'xhr' => ''
        )
    );
    protected $_tags = array(
        'meta' => '<meta%s/>',
        'metalink' => '<link href="%s"%s/>',
        'link' => '<a href="%s"%s>%s</a>',
        'mailto' => '<a href="mailto:%s" %s>%s</a>',
        'form' => '<form action="%s"%s>',
        'formend' => '</form>',
        'input' => '<input name="%s"%s/>',
        'textarea' => '<textarea name="%s"%s>%s</textarea>',
        'hidden' => '<input type="hidden" name="%s"%s/>',
        'checkbox' => '<input type="checkbox" name="%s" %s/>',
        'checkboxmultiple' => '<input type="checkbox" name="%s[]"%s />',
        'radio' => '<input type="radio" name="%s" id="%s"%s />%s',
        'selectstart' => '<select name="%s"%s>',
        'selectmultiplestart' => '<select name="%s[]"%s>',
        'selectempty' => '<option value=""%s>&nbsp;</option>',
        'selectoption' => '<option value="%s"%s>%s</option>',
        'selectend' => '</select>',
        'optiongroup' => '<optgroup label="%s"%s>',
        'optiongroupend' => '</optgroup>',
        'checkboxmultiplestart' => '',
        'checkboxmultipleend' => '',
        'password' => '<input type="password" name="%s" %s/>',
        'file' => '<input type="file" name="%s" %s/>',
        'file_no_model' => '<input type="file" name="%s" %s/>',
        'submit' => '<input %s/>',
        'submitimage' => '<input type="image" src="%s" %s/>',
        'button' => '<button%s>%s</button>',
        'image' => '<img src="%s" %s/>',
        'tableheader' => '<th%s>%s</th>',
        'tableheaderrow' => '<tr%s>%s</tr>',
        'tablecell' => '<td%s>%s</td>',
        'tablerow' => '<tr%s>%s</tr>',
        'block' => '<div%s>%s</div>',
        'blockstart' => '<div%s>',
        'blockend' => '</div>',
        'hiddenblock' => '<div style="display:none;">%s</div>',
        'tag' => '<%s%s>%s</%s>',
        'tagstart' => '<%s%s>',
        'tagend' => '</%s>',
        'tagselfclosing' => '<%s%s/>',
        'para' => '<p%s>%s</p>',
        'parastart' => '<p%s>',
        'label' => '<label for="%s"%s>%s</label>',
        'fieldset' => '<fieldset%s>%s</fieldset>',
        'fieldsetstart' => '<fieldset><legend>%s</legend>',
        'fieldsetend' => '</fieldset>',
        'legend' => '<legend>%s</legend>',
        'css' => '<link rel="%s" type="text/css" href="%s" %s/>',
        'style' => '<style type="text/css"%s>%s</style>',
        'charset' => '<meta http-equiv="Content-Type" content="text/html; charset=%s" />',
        'ul' => '<ul%s>%s</ul>',
        'ol' => '<ol%s>%s</ol>',
        'li' => '<li%s>%s</li>',
        'error' => '<div%s>%s</div>',
        'javascriptblock' => '<script%s>%s</script>',
        'javascriptstart' => '<script>',
        'javascriptlink' => '<script type="text/javascript" src="%s"%s></script>',
        'javascriptend' => '</script>'
    );
    protected $_optionMap = array(
        'request' => array(
            'type' => 'dataType',
            'before' => 'beforeSend',
            'method' => 'type',
        ),
        'sortable' => array(
            'complete' => 'stop',
        ),
        'drag' => array(
            'snapGrid' => 'grid',
            'container' => 'containment',
        ),
        'drop' => array(
            'leave' => 'out',
            'hover' => 'over'
        ),
        'slider' => array(
            'complete' => 'stop',
            'direction' => 'orientation'
        )
    );
    public $_blocks = array(); 
    public $_active = array(); 
    /**
    * Constructor for Model
    * 
    */
    public function __construct($controller , $action) {
      
        $this->helper = get_class($this);
        $this->_controller = $controller;
        $this->_action = $action;
        
        $this->init();
    }
    public function init(){

    }

    
    public function object($data = array(), $options = array()){
        $defaultOptions = array(
            'prefix' => '', 'postfix' => '',
        );
        $options += $defaultOptions;

        return $options['prefix'] . json_encode($data) . $options['postfix'];   
    }

    

    public function ajaxlink($title, $url = null, $options = array()) {


        if (!isset($options['id'])) {
            $options['id'] = 'link-' . intval(mt_rand());
        }
        list($options, $htmlOptions) = $this->_getHtmlOptions($options);
        $out = $this->urllink($title, $url, $htmlOptions);
        $this->get('#' . $htmlOptions['id']);
        $requestString = $event = '';
        
        $buffer = isset($options['buffer']) ? $options['buffer'] : null;
        $safe = isset($options['safe']) ? $options['safe'] : true;
        unset($options['buffer'], $options['safe']);

        $requestString .= $this->request($url, $options);
        
        if(!isset($options['event'])){
            $event = $this->event('click', $requestString, $options + array('buffer' => $buffer));
        
        }else{
            $event = $this->event($options['event'], $requestString, $options + array('buffer' => $buffer));
        
        }    

        
       $out .= $this->scriptBlock($event);
      
          
        return $out;
    }

    public function redirect($url = null) {
        return 'window.location = "' . $url. '";';
    }

    public function event($type,  $callback, $options = array()) {
        $defaults = array('wrap' => true, 'stop' => true);
        $options += $defaults;

        $function = 'function (event) {%s}';
        if ($options['wrap'] && $options['stop']) {
            $callback .= "\nreturn false;";
        }
        if ($options['wrap']) {
            $callback = sprintf($function, $callback);
        }
        return sprintf('%s.bind("%s", %s);', $this->selection, $type, $callback);
    }
    protected function _getHtmlOptions($options, $additional = array()) {
        $htmlKeys = array_merge(
            array('class', 'id', 'escape', 'onblur', 'onfocus', 'rel', 'title', 'style'),
            $additional
        );
        $htmlOptions = array();
        foreach ($htmlKeys as $key) {
            if (isset($options[$key])) {
                $htmlOptions[$key] = $options[$key];
            }
            unset($options[$key]);
        }
        if (isset($options['htmlAttributes'])) {
            $htmlOptions = array_merge($htmlOptions, $options['htmlAttributes']);
            unset($options['htmlAttributes']);
        }
        return array($options, $htmlOptions);
    }

    public function urllink($title, $url = null, $options = array(), $confirmMessage = false) {
        $escapeTitle = true;
        

        if (isset($options['escapeTitle'])) {
            $escapeTitle = $options['escapeTitle'];
            unset($options['escapeTitle']);
        } elseif (isset($options['escape'])) {
            $escapeTitle = $options['escape'];
        }

        if ($escapeTitle === true) {
            $title = htmlspecialchars($title);
        } elseif (is_string($escapeTitle)) {
            $title = htmlentities($title, ENT_QUOTES, $escapeTitle);
        }

        if (!empty($options['confirm'])) {
            $confirmMessage = $options['confirm'];
            unset($options['confirm']);
        }
        if (isset($options['default']) && !$options['default']) {
            if (isset($options['onclick'])) {
                $options['onclick'] .= ' ';
            } else {
                $options['onclick'] = '';
            }
            $options['onclick'] .= 'event.returnValue = false; return false;';
            unset($options['default']);
        }
        return sprintf($this->_tags['link'], $url, $this->_parseAttributes($options), $title);
    }

    protected function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
        if (!is_string($options)) {
            $options = (array)$options + array('escape' => true);

            if (!is_array($exclude)) {
                $exclude = array();
            }

            $exclude = array('escape' => true) + array_flip($exclude);
            $escape = $options['escape'];
            $attributes = array();

            foreach ($options as $key => $value) {
                if (!isset($exclude[$key]) && $value !== false && $value !== null) {
                    $attributes[] = $this->_formatAttribute($key, $value, $escape);
                }
            }
            $out = implode(' ', $attributes);
        } else {
            $out = $options;
        }
        return $out ? $insertBefore . $out . $insertAfter : '';
    }

    protected function _formatAttribute($key, $value, $escape = true) {
        if (is_array($value)) {
            $value = implode(' ', $value);
        }
        if (is_numeric($key)) {
            return sprintf($this->_minimizedAttributeFormat, $value, $value);
        }
        $truthy = array(1, '1', true, 'true', $key);
        $isMinimized = in_array($key, $this->_minimizedAttributes);
        if ($isMinimized && in_array($value, $truthy, true)) {
            return sprintf($this->_minimizedAttributeFormat, $key, $key);
        }
        if ($isMinimized) {
            return '';
        }
        return sprintf($this->_attributeFormat, $key, ($escape ? htmlspecialchars($value) : $value));
    }

    public function get($selector) {
        if ($selector === 'window' || $selector === 'document') {
            $this->selection = $this->jQueryObject . '(' . $selector . ')';
        } else {
            $this->selection = $this->jQueryObject . '("' . $selector . '")';
        }
        return $this;
    }

    public function scriptBlock($script, $options = array()) {
        $options += array('type' => 'text/javascript', 'safe' => true, 'inline' => true);
        


        if ($options['safe']) {
            $script = "\n" . '//<![CDATA[' . "\n" . $script . "\n" . '//]]>' . "\n";
        }
                if (!$options['inline'] && empty($options['block'])) {
            $options['block'] = 'script';
        }

        unset($options['inline'], $options['safe']);

        $attributes = $this->_parseAttributes($options, array('block'), ' ');
        

        $out = sprintf("<script%s>%s</script>", $attributes, $script);

        return $out;
    }

    public function concat($name, $value = null, $mode = "append") {
        if (isset($value)) {
            if (!isset($this->_blocks[$name])) {
                $this->_blocks[$name] = '';
            }
            if ($mode === "prepend") {
                $this->_blocks[$name] = $value . $this->_blocks[$name];
            } else {
                $this->_blocks[$name] .= $value;
            }
        } else {
            if (!n_array($name, $this->_active)) {
           
                $this->_active[] = $name;
                ob_start();
                }
            }
        }

    public function request($url, $options = array()) {


        $url = html_entity_decode($url, ENT_COMPAT, 'UTF-8');
        $options = $this->_mapOptions('request', $options);
 
        if (isset($options['data']) && is_array($options['data'])) {
            $options['data'] = $this->_toQuerystring($options['data']);
        }
        $options['url'] = $url;
        if (isset($options['update'])) {
            $wrapCallbacks = isset($options['wrapCallbacks']) ? $options['wrapCallbacks'] : true;
            $success = '';
            if (isset($options['success']) && !empty($options['success'])) {
                $success .= $options['success'];
            }
            $success .= $this->jQueryObject . '("' . $options['update'] . '").html(data);';
            if (!$wrapCallbacks) {
                $success = 'function (data, textStatus) {' . $success . '}';
            }
            $options['dataType'] = 'html';
            $options['success'] = $success;
            unset($options['update']);
        }
        $callbacks = array('success', 'error', 'beforeSend', 'complete', 'xhr');
        if (!empty($options['dataExpression'])) {
            $callbacks[] = 'data';
            unset($options['dataExpression']);
        }
        $options = $this->_prepareCallbacks('request', $options);
       
         $options = $this->_parseOptions($options, $callbacks);
        return $this->jQueryObject . '.ajax({' . $options . '});';
    }

    protected function _mapOptions($method, $options) {
        if (!isset($this->_optionMap[$method])) {
            return $options;
        }
        foreach ($this->_optionMap[$method] as $abstract => $concrete) {
            if (isset($options[$abstract])) {
                $options[$concrete] = $options[$abstract];
                unset($options[$abstract]);
            }
        }
        return $options;
    }

    protected function _toQuerystring($parameters) {
        $out = '';
        $keys = array_keys($parameters);
        $count = count($parameters);
        for ($i = 0; $i < $count; $i++) {
            $out .= $keys[$i] . '=' . $parameters[$keys[$i]];
            if ($i < $count - 1) {
                $out .= '&';
            }
        }
        return $out;
    }

    protected function _prepareCallbacks($method, $options, $callbacks = array()) {
        $wrapCallbacks = true;
        if (isset($options['wrapCallbacks'])) {
            $wrapCallbacks = $options['wrapCallbacks'];
        }
        unset($options['wrapCallbacks']);
        if (!$wrapCallbacks) {
            return $options;
        }
        $callbackOptions = array();
        if (isset($this->_callbackArguments[$method])) {
            $callbackOptions = $this->_callbackArguments[$method];
        }
        $callbacks = array_unique(array_merge(array_keys($callbackOptions), (array)$callbacks));

        foreach ($callbacks as $callback) {
            if (empty($options[$callback])) {
                continue;
            }
            $args = null;
            if (!empty($callbackOptions[$callback])) {
                $args = $callbackOptions[$callback];
            }
            $options[$callback] = 'function (' . $args . ') {' . $options[$callback] . '}';
        }
        return $options;
    }

    protected function _parseOptions($options, $safeKeys = array()) {


        $out = array();
        $safeKeys = array_flip($safeKeys);

        foreach($options as $key => $value) {
          //  print_r($key);
            if (!is_int($value) && !isset($safeKeys[$key])) {
                $value = $this->value($value);
            }
            $out[] = $key . ':' . $value;
        }

        sort($out);
        return implode(', ', $out);
    }

    public function value($val = array(), $quoteString = null, $key = 'value') {
        if ($quoteString === null) {
            $quoteString = true;
        }
        return $this->jsvalue($val, $quoteString);
    }
    public function jsvalue($val = array(), $quoteString = null, $key = 'value') {
        if ($quoteString === null) {
            $quoteString = true;
        }
        switch (true) {
            case (is_array($val) || is_object($val)):
                $val = $this->object($val);
                break;
            case ($val === null):
                $val = 'null';
                break;
            case (is_bool($val)):
                $val = ($val === true) ? 'true' : 'false';
                break;
            case (is_int($val)):
                $val = $val;
                break;
            case (is_float($val)):
                $val = sprintf("%.11f", $val);
                break;
            default:
                $val = $this->escape($val);
                if ($quoteString) {
                    $val = '"' . $val . '"';
                }
        }
       
        return $val;
    }

    public function escape($string) {
        return $this->_utf8ToHex($string);
    }
    protected function _utf8ToHex($string) {
        $length = strlen($string);
        $return = '';
        for ($i = 0; $i < $length; ++$i) {
            $ord = ord($string{$i});
            switch (true) {
                case $ord == 0x08:
                    $return .= '\b';
                    break;
                case $ord == 0x09:
                    $return .= '\t';
                    break;
                case $ord == 0x0A:
                    $return .= '\n';
                    break;
                case $ord == 0x0C:
                    $return .= '\f';
                    break;
                case $ord == 0x0D:
                    $return .= '\r';
                    break;
                case $ord == 0x22:
                case $ord == 0x2F:
                case $ord == 0x5C:
                    $return .= '\\' . $string{$i};
                    break;
                case (($ord >= 0x20) && ($ord <= 0x7F)):
                    $return .= $string{$i};
                    break;
                case (($ord & 0xE0) == 0xC0):
                    if ($i + 1 >= $length) {
                        $i += 1;
                        $return .= '?';
                        break;
                    }
                    $charbits = $string{$i} . $string{$i + 1};
                    $char = $this->utf8($charbits);
                    $return .= sprintf('\u%04s', dechex($char[0]));
                    $i += 1;
                    break;
                case (($ord & 0xF0) == 0xE0):
                    if ($i + 2 >= $length) {
                        $i += 2;
                        $return .= '?';
                        break;
                    }
                    $charbits = $string{$i} . $string{$i + 1} . $string{$i + 2};
                    $char = $this->utf8($charbits);
                    $return .= sprintf('\u%04s', dechex($char[0]));
                    $i += 2;
                    break;
                case (($ord & 0xF8) == 0xF0):
                    if ($i + 3 >= $length) {
                        $i += 3;
                        $return .= '?';
                        break;
                    }
                    $charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3};
                    $char = $this->utf8($charbits);
                    $return .= sprintf('\u%04s', dechex($char[0]));
                    $i += 3;
                    break;
                case (($ord & 0xFC) == 0xF8):
                    if ($i + 4 >= $length) {
                        $i += 4;
                        $return .= '?';
                        break;
                    }
                    $charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4};
                    $char = $this->utf8($charbits);
                    $return .= sprintf('\u%04s', dechex($char[0]));
                    $i += 4;
                    break;
                case (($ord & 0xFE) == 0xFC):
                    if ($i + 5 >= $length) {
                        $i += 5;
                        $return .= '?';
                        break;
                    }
                    $charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4} . $string{$i + 5};
                    $char = $this->utf8($charbits);
                    $return .= sprintf('\u%04s', dechex($char[0]));
                    $i += 5;
                    break;
            }
        }
        return $return;
    }


    public static function utf8($string) {
        $map = array();

        $values = array();
        $find = 1;
        $length = strlen($string);

        for ($i = 0; $i < $length; $i++) {
            $value = ord($string[$i]);

            if ($value < 128) {
                $map[] = $value;
            } else {
                if (empty($values)) {
                    $find = ($value < 224) ? 2 : 3;
                }
                $values[] = $value;

                if (count($values) === $find) {
                    if ($find == 3) {
                        $map[] = (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64);
                    } else {
                        $map[] = (($values[0] % 32) * 64) + ($values[1] % 64);
                    }
                    $values = array();
                    $find = 1;
                }
            }
        }
        return $map;
    }

    // public function object($data = array(), $options = array()) {
    //     $defaultOptions = array(
    //         'prefix' => '', 'postfix' => '',
    //     );
    //     $options += $defaultOptions;

    //     return $options['prefix'] . json_encode($data) . $options['postfix'];
    // }
//mai fucntion
    public function link($title, $url = null, $runevent = null ){
        if (!isset($options['id'])) {
            $options['id'] = 'link-' . intval(mt_rand());
        }
        list($options, $htmlOptions) = $this->_getHtmlOptions($options);
        $out = $this->Html->link($title, $url, $htmlOptions);
        $this->get('#' . $htmlOptions['id']);
        $requestString =  $event = '';
        // if (isset($options['confirm'])) {
        //     $requestString = $this->confirmReturn($options['confirm']);
        //     unset($options['confirm']);
        // }
        $buffer = isset($options['buffer']) ? $options['buffer'] : null;
        $safe = isset($options['safe']) ? $options['safe'] : true;
        unset($options['buffer'], $options['safe']);

        $requestString .= $this->request($url, $options);

        if (!empty($requestString)) {
            if($runevent == null){
                $event = $this->event('click', $requestString, $options + array('buffer' => $buffer));
            
            }
            $event = $this->event($runevent, $requestString, $options + array('buffer' => $buffer));
            
        }
        if (isset($buffer) && !$buffer) {
            $opts = array('safe' => $safe);
            $out .= $this->scriptBlock($event, $opts);
        }
        return $out;
    }
}
